package com.meihua.baoleyi.utils;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.widget.ImageView;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * Created by liming on 2017/10/3.
 * 图片的三级缓存
 */
public class ImageLoader {

	//下载任务执行完毕后发送的message的what
	public static final int LOAD_FINISH=101;
	public static final int NEW_TASK=102;
	/**线程池中线程的数量*/
	public static int Thread_Count;
	public static Context c;//上下文
	//内存缓存
	public static LruCache<String,Bitmap> Memc;
	//双向队列。未来可以根据需求选择是FIFO还是LIFO
	public static LinkedBlockingDeque<Runnable> tashQueue;
	//线程池
	public static ExecutorService exec;
	//从任务队列中取下载任务的handler
	public static Handler pollHandler;
	//任务下载完毕后，用来刷新ListView条目显示下载图
	public static Handler uiHandler;
	//养活pollHandler
	public static Thread pollThread;
	//标示作用，用来保证imageLoader只要执行一次初始化操作
	public static boolean isFirstTime = true;
	//磁盘缓存
	public static DiskLruCache Diskcache;

	public static void init(Context context)
	{
		//判断是否初始化过
		if (!isFirstTime) {
			return;
		}
		c=context;
		Thread_Count=getNumberOfCores();
		Memc=new LruCache<String, Bitmap>((int) Runtime.getRuntime().maxMemory()/8){
			@Override
			protected int sizeOf(String key, Bitmap value) {
				return value.getHeight()*value.getRowBytes();
			}
		};
		//初始化磁盘缓存
		try {
			Diskcache= DiskLruCache.open(directory(), appVersion(), 1, 1024 * 1024 * 10);
		} catch (IOException e1) {
			e1.printStackTrace();
		}

		//初始化任务队列
		tashQueue=new LinkedBlockingDeque<Runnable>();
		//初始化线程池对象
		exec= Executors.newFixedThreadPool(Thread_Count);
//		exec=Executors.newSingleThreadExecutor();//不能指定线程数量

		uiHandler=new Handler(Looper.getMainLooper())
		{
			@Override
			public void handleMessage(Message msg) {
				//利用线程池中执行完毕的内容，来刷新ListView界面
				if (msg.what==LOAD_FINISH) {
					//msg.obj放什么能解决listview加载”动画“现象
					ValueObject vo=(ValueObject) msg.obj;
					ImageView iv=vo.iv;
					String url=vo.url;
					Bitmap bitmap=vo.bitmap;
					if (iv.getTag().toString().equals(url)) {
						iv.setImageBitmap(bitmap);
					}
				}else {
					super.handleMessage(msg);
				}
			}
		};
		pollThread=new Thread(){
			@Override
			public void run() {
				Looper.prepare();

				pollHandler=new Handler(){
					@Override
					public void handleMessage(Message msg) {
						if (msg.what==NEW_TASK) {
							//现在在任务队列中被放入了新“下载任务
							//取队列中取任务
							try {
								Runnable take = tashQueue.takeFirst();
								exec.execute(take);
							} catch (InterruptedException e) {
								e.printStackTrace();
							}
						}else {
							super.handleMessage(msg);
						}
					}
				};
				Looper.loop();
			}
		};
		pollThread.start();
		//初始化完毕
		isFirstTime=false;

		Log.d("TAG", "Thread_Count="+Thread_Count);
	}

	/**磁盘缓存版本号*/
	private static int appVersion() {
		try {
			PackageInfo info=c.getPackageManager().getPackageInfo(
					c.getPackageName(), 0);
			return info.versionCode;
		} catch (NameNotFoundException e) {
			e.printStackTrace();
		}
		return 1;
	}
	/**磁盘缓存路径*/
	private static File directory() {
		String path=c.getCacheDir().getPath();
		return new File(path,"imageloadercache");
	}
	/**
	 * 获取uri指向的图像，放到iv中显示
	 * @param url 图像路径
	 * @param iv 显示图像的ImageView
	 */
	public static void loadImage(final String url, final ImageView iv)
	{
		if (isFirstTime) {
			//异常是一种特殊的返回值
			throw new RuntimeException("ImageLoader未初始化");
		}
		//先判断url 所指向的图像在缓存中是否有缓存
		//url转为MD5格式的字符串
		final String md5Url=getMD5(url);
		//设置TAG，到时候要与vo中的url进行比较
		iv.setTag(md5Url);

		Bitmap bitmap=Memc.get(md5Url);
		if (bitmap!=null) {
//			Log.d("TAG", "图像是从缓存中加载");
//			Log.d("TAG", "huancun");
			iv.setImageBitmap(bitmap);
			return;
		}
		try {
			//从磁盘缓存中取
			DiskLruCache.Snapshot snap = Diskcache.get(md5Url);
			if (snap!=null) {
//				Log.d("TAG", "图像是从磁盘缓存中加载");
//				Log.d("TAG", "cipan");
				InputStream in = snap.getInputStream(0);
				bitmap= BitmapFactory.decodeStream(in);
				//从磁盘放到内存缓存中
				Memc.put(md5Url, bitmap);
				iv.setImageBitmap(bitmap);
				return ;
			}
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		//从网络中下载
		tashQueue.add(new Runnable() {
			@Override
			public void run() {
				//下载任务
				//发起网络连接，获得图片资源
				try {
					URL u=new URL(url);
					HttpURLConnection conn=(HttpURLConnection) u.openConnection();
					conn.setDoInput(true);
					conn.setRequestMethod("GET");
					conn.connect();

					InputStream in = conn.getInputStream();
					//要对图像进行适当的压缩
					Bitmap bitmap=compress(in,iv);
					in.close();
					//将压缩后的图像放到内存缓存中
					Memc.put(md5Url, bitmap);
					Log.d("TAG", "net");
					//将压缩后的图片放到磁盘缓存中
					DiskLruCache.Editor edit = Diskcache.edit(md5Url);
					OutputStream out = edit.newOutputStream(0);
					bitmap.compress(CompressFormat.JPEG, 100, out);
					edit.commit();
//					out.close();
					Diskcache.flush();//写日志，可选操作
					ValueObject vo=new ValueObject(iv, md5Url, bitmap);
					Message.obtain(uiHandler, LOAD_FINISH,vo).sendToTarget();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		});
		Message.obtain(pollHandler, NEW_TASK).sendToTarget();
	}
	/**
	 * 根据要显示的imageview的大小对图像进行压缩
	 * @param in 图像源
	 * @param iv 未来要显示的图像的imageview
	 * @return
	 */
	protected static Bitmap compress(InputStream in, ImageView iv) {
		Bitmap bitmap=null;
		try {
			//1. 先获得原始图像的尺寸大小
			//借助Options来获得图像的大小
			ByteArrayOutputStream out=new ByteArrayOutputStream();
			byte[] buff=new byte[4096];
			int len=-1;
			while ((len=in.read(buff))!=-1) {
				out.write(buff,0,len);
			}
			Options opts=new Options();
			opts.inJustDecodeBounds=true;
			//opts一旦设置了inJustDecodeBounds = true，
			//BitmapFactory是不会返回bitmap对象，只会null
			BitmapFactory.decodeByteArray(out.toByteArray(), 0, out.toByteArray().length,opts);
			int width=opts.outWidth;//图像的宽度
			int height=opts.outHeight;//图像的高度
			//2. 获取希望的宽度高度
			int targetWidth=iv.getWidth();//imageview的宽度
			int targetHeight=iv.getHeight();//imageview 的高度
			//如果去imageview的高宽取不到，可以手动指定，也可以以当前设备的宽高作为压缩目标
			if (targetHeight==0||targetWidth==0) {
				//可以手动指定一个值
				//或者用屏幕的宽高
				targetWidth=c.getResources().getDisplayMetrics().widthPixels;
				targetHeight=c.getResources().getDisplayMetrics().heightPixels;
			}

			//3.计算压缩比
			int sampleSize=1;

			if (width*1.0/targetWidth>1||height*1.0/targetHeight>1) {
//				sampleSize=(int) Math.ceil(Math.max(width*1.0/targetWidth, height*1.0/targetHeight));
				sampleSize=(int) Math.ceil(Math.max(width*1.0/targetWidth, height*1.0/targetHeight));
			}
			//4.压缩图像 
			opts.inSampleSize=sampleSize;
			opts.inJustDecodeBounds=false;
			bitmap= BitmapFactory.decodeByteArray(out.toByteArray(), 0, out.toByteArray().length, opts);
			out.close();

		} catch (Exception e) {
			e.printStackTrace();
		}
		return bitmap;
	}

	/**MD5对图像URL加密*/
	private static String getMD5(String url) {
		String result="";
		try {
			MessageDigest md= MessageDigest.getInstance("MD5");
			md.update(url.getBytes());
			byte[] bytes = md.digest();
			StringBuilder sb=new StringBuilder();
			for (byte b : bytes) {
				String str= Integer.toHexString(b & 0xFF);
				if (str.length()==1) {
					sb.append("0");
				}
				sb.append(str);
			}
			result=sb.toString();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		return result;
	}

	/**根据手机处理器核数量，计算线程池中线程数量*/
	private static int getNumberOfCores() {

		File file=new File("/sys/devices/system/cpu/");
		File[] files = file.listFiles(new FilenameFilter() {
			@Override
			public boolean accept(File dir, String filename) {
				if (filename.matches("cpu[0-9]")) {
					return true;
				}else {
					return false;
				}
			}
		});
		if (files.length>0) {
			return files.length;
		}else {
			return 1;
		}
	}

	private static class ValueObject
	{
		ImageView iv;
		String url;
		Bitmap bitmap;
		public ValueObject(ImageView iv, String url, Bitmap bitmap) {
			super();
			this.iv = iv;
			this.url = url;
			this.bitmap = bitmap;
		}
	}

}
